using System;
using System.Collections;

namespace System.IO
{
	/// <summary>
	/// A stream that uses a ByteQueue for its backing store.
	/// </summary>
	public class ByteQueueStream : System.IO.Stream
	{
		#region Static Stuff
		private const string bqs_noread	= "This ByteQueueStream is not allowing Read operations.";
		private const string bqs_nowrite= "This ByteQueueStream is not allowing Write operations.";
		private const string bqs_noseek = "The ByteQueueStream does not support Seek operations."; 
		private const string bqs_closed	= "The ByteQueueStream has been closed!";
		/// <summary>
		/// Gets a ByteQueueStream with no backing store.
		/// </summary>
		public new static ByteQueueStream Null
		{
			get
			{
				return new ByteQueueStream();
			}
		}
		#endregion

		#region Private Variables
		private IQueue<byte>	_ByteQueue;
		private bool		_CanRead;
		private bool		_CanWrite;
		private bool		_IsNull;
		private bool		_IsClosed;
		#endregion

		#region Constructors
		/// <summary>
		/// The default constructor for ByteQueueStream
		/// </summary>
		private ByteQueueStream()
		{
			_IsNull = true;
			_ByteQueue = null;
			_CanRead = false;
			_CanWrite = false;
		}
		/// <summary>
		/// Instanciates an instance of ByteQueueStream using the given byte queue,
		/// with allowances for both reading and writing operations
		/// </summary>
		public ByteQueueStream(IQueue<byte> byteQueue)
			:this(byteQueue,true,true)
		{
		}
		/// <summary>
		/// Instanciates an instance of ByteQueueStream with specification as to whether
		/// or not reading and writing is allowed to the stream
		/// </summary>
		/// <param name="byteQueue">The byte queue for the backing store</param>
		/// <param name="canRead">Whether or not the stream is to be readable</param>
		/// <param name="canWrite">Whether or not the stream is to be writable</param>
		public ByteQueueStream(IQueue<byte> byteQueue, bool canRead, bool canWrite)
		{
			if(byteQueue==null)
				throw new ArgumentNullException("byteQueue");

			this._ByteQueue =	byteQueue;
			this._CanRead	=	canRead;
			this._CanWrite	=	canWrite;
		}
		#endregion

		#region Funtionality Detection
		/// <summary>
		/// Whether or not the ByteQueueStream is readable.
		/// </summary>
		public override bool CanRead
		{
			get
			{
				return this._CanRead;
			}
		}
		/// <summary>
		/// Whether or not the ByteQueueStream supports seek operations. Always returns false.
		/// </summary>
		public override bool CanSeek
		{
			get
			{
				return false;
			}
		}
		/// <summary>
		/// Whether or not the ByteQueueStream writable.
		/// </summary>
		public override bool CanWrite
		{
			get
			{
				return this._CanWrite;
			}
		}

		#endregion
		
		#region Maintenance
		/// <summary>
		/// Closes the ByteQueueStream.
		/// </summary>
		public override void Close()
		{
			base.Close();
			_ByteQueue = null;
			_CanRead = false;
			_CanWrite = false;
			_IsClosed = true;
		}

		/// <summary>
		/// Nothing to do here, move along.
		/// </summary>
		public override void Flush()
		{
		}

		#endregion
		
		#region Equality
		public override bool Equals(object obj)
		{
			if(obj.GetType()==this.GetType())
				return Equals((ByteQueueStream)obj);
			else
				return base.Equals(obj);
		}

        public override int GetHashCode()
        {
            return this._ByteQueue.GetHashCode();
        }
		
		/// <summary>
		/// Tests to see if the given ByteQueueStream is equal to this one, by testing for equality
		/// between the two underlying ByteQueue's.
		/// </summary>
		/// <param name="obj">The object to test for equality against.</param>
		/// <returns>Returns true if the ByteQueueStreams are equal, otherwise false.</returns>
		public bool Equals(ByteQueueStream obj)
		{
			if(this._IsNull==true && obj._IsNull==true)
				return true;
			else if(this._IsNull==true && obj==null)
				return true;
			else if((this._ByteQueue==obj._ByteQueue)&&(this._ByteQueue!=null))
				return true;
			else
				return false;
		}
		#endregion

		#region Seeking
		/// <summary>
		/// Gets the length of the stream. Always throws InvalidOperatonException.
		/// </summary>
		public override long Length
		{
			get
			{
				throw new InvalidOperationException(ByteQueueStream.bqs_noseek);
			}
		}
		/// <summary>
		/// Gets/Sets the position within the stream. Always throws InvalidOperatonException.
		/// </summary>
		public override long Position
		{
			get
			{
				throw new InvalidOperationException(ByteQueueStream.bqs_noseek);
			}
			set
			{
				throw new InvalidOperationException(ByteQueueStream.bqs_noseek);
			}
		}
		/// <summary>
		/// Sets the length of the stream. Always throws InvalidOperatonException.
		/// </summary>
		public override void SetLength(long value)
		{
			throw new InvalidOperationException(ByteQueueStream.bqs_noseek);
		}
		/// <summary>
		/// Changes the current location in the stream. Always throws InvalidOperatonException.
		/// </summary>
		public override long Seek(long offset, SeekOrigin origin)
		{
			throw new InvalidOperationException(ByteQueueStream.bqs_noseek);
		}
		#endregion

		#region Reading
		public override int Read(byte[] buffer, int offset, int count)
		{
			if(this._CanWrite==false)
				throw new InvalidOperationException(ByteQueueStream.bqs_noread);
			if(this._IsClosed==true)
				throw new InvalidOperationException(ByteQueueStream.bqs_closed);		
			if(this._IsNull)
				return 0;

			int x;
			for(x=offset;((x<(offset+count))&&(this._ByteQueue.Count>0));x++)
			{
				buffer[x] = _ByteQueue.Dequeue();
			}
			return (x - offset);
		}
		public override int ReadByte()
		{
			if(this._CanWrite==false)
				throw new InvalidOperationException(ByteQueueStream.bqs_noread);
			if(this._IsClosed==true)
				throw new InvalidOperationException(ByteQueueStream.bqs_closed);
			if(this._IsNull)
				return -1;

			if(this._ByteQueue.Count>0)
				return (int)_ByteQueue.Dequeue();
			else
				return -1;
		}
		#endregion

		#region Writing
		public override void Write(byte[] buffer, int offset, int count)
		{
			if(this._CanWrite==false)
				throw new InvalidOperationException(ByteQueueStream.bqs_nowrite);
			if(this._IsClosed==true)
				throw new InvalidOperationException(ByteQueueStream.bqs_closed);
			if(this._IsNull)
				return;

			for(int x=offset;x<(offset+count);x++)
			{
				_ByteQueue.Enqueue(buffer[x]);
			}
		}
		public override void WriteByte(byte value)
		{
			if(this._CanWrite==false)
				throw new InvalidOperationException(ByteQueueStream.bqs_nowrite);
			if(this._IsClosed==true)
				throw new InvalidOperationException(ByteQueueStream.bqs_closed);
			if(this._IsNull)
				return;

			_ByteQueue.Enqueue(value);
		}
		#endregion
	}
}